﻿using oceanho.webapi.factory.httpCore;
using oceanho.webapi.factory.httpUtility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;

namespace oceanho.webapi.factory.httpEntity
{
    /// <summary>
    /// 定义一个描述文件上传的参数类，它包含了上传文件时所必须的参数描述与限制信息
    /// </summary>
    public class UploadParameter
    {
        /// <summary>
        /// 文件上传的参数类
        /// </summary>
        /// <param name="fileUploads">上传文件描述对象列表</param>
        /// <param name="saveToDir">上传文件保存的磁盘目录(如：X:\\xxx\\xxx)</param>
        /// <param name="fileTotalSize">上传文件总结大小</param>
        public UploadParameter(UploadItem[] fileUploads, string saveToDir, long fileTotalSize)
        {

            this.SaveToDir = saveToDir;
            this.FileUploads = fileUploads;
            this.FileTotalSize = fileTotalSize;

            this.AutoMKDir = FileUploadDefault.AutoMKDir;
            this.BufferSize = FileUploadDefault.BufferSize;
                       

            this.AllowFileSize = FileUploadDefault.AllowFileSize;            
            this.AllowFileCount = FileUploadDefault.AllowFileCount;
            this.AllowSingleFileSize = FileUploadDefault.AllowSingleFileSize;

            this.FileTypes = new List<string>();
            this.FileTypes.AddRange(FileUploadDefault.UnOrAllowTypes);

            this.IsUseTranstion = FileUploadDefault.IsUseTranstion;
            this.FileUploadMode = FileUploadDefault.FileUploadModeD;

            this.TypeValidMode = FileUploadDefault.FileUploadTypeValidModeD;
            this.FileExistDoOption = FileUploadDefault.FileUploadExistDoOptionD;
        }

        #region -- 字段定义 --

        // 定义保存 事件订阅者的 EventHandlerList
        private readonly EventHandlerList Events = new EventHandlerList();
        #endregion

        #region -- 属性定义 --

        /// <summary>
        /// 获取或设置上传文件Type的校验模式（是：只允许/不允许），默认值：FileUploadTypeValidMode.AllowType
        /// </summary>
        public FileUploadTypeValidMode TypeValidMode { get; set; }

        /// <summary>
        /// 获取或设置上传文件如果存在于服务器时的操作选项（是：只允许/不允许），默认值：FileUploadExistDoOption.ThrowEx
        /// </summary>
        public FileUploadExistDoOption FileExistDoOption { get; set; }

        /// <summary>
        /// 获取或者设置上传文件（允许/不允许）的文件类型，具体是禁止还是允许，需要配合TypeValidMode属性确定
        /// </summary>
        public List<string> FileTypes { get; set; }

        /// <summary>
        /// 获取或设置允许所有上传文件的最大大小（单位：字节），默认（20 MB）
        /// </summary>
        public long AllowFileSize { get; set; }

        /// <summary>
        /// 获取或设置允许单个上传文件的最大大小（单位：字节），默认（5 MB）
        /// </summary>
        public long AllowSingleFileSize { get; set; }

        /// <summary>
        /// 获取或设置允许所有上传文件的最大文件个数，默认（20个）
        /// </summary>
        public uint AllowFileCount { get; set; }

        /// <summary>
        /// 获取或设置服务器保存上传文件的目录（如：X:\\www\\aaa\\）
        /// </summary>
        public string SaveToDir { get; set; }

        /// <summary>
        /// 获取或者设置是否允许自动创建目录（如果目录不存在的情况下，默认为true）
        /// </summary>
        public bool AutoMKDir { get; set; }

        /// <summary>
        /// 获取或者设置上传文件每次写入磁盘的数据大小（单位，字节，默认：FileUploadDefault.BufferSize）
        /// </summary>
        public int BufferSize { get; set; }

        /// <summary>
        /// 获取或设置支持的文件上传模式（单文件/多文件），默认只允许单文件上传（Single）
        /// </summary>
        public FileUploadMode FileUploadMode { get; set; }

        /// <summary>
        /// 获取或者设置上传文件是否使用事物模式（如果某个文件上传失败，对于此处请求上传的其它文件也会自动删除），默认为true（不自动删除在失败之前已经上传的文件）
        /// </summary>
        public bool IsUseTranstion { get; set; }

        /// <summary>
        /// 获取客户端上传文件的列表对象
        /// </summary>
        public UploadItem[] FileUploads { get; private set; }

        /// <summary>
        /// 获取或者设置一个值，该值表示所有上传文件的文件大小（单位：字节）
        /// </summary>
        public long FileTotalSize { get; private set; }
        #endregion

        #region -- 委托/事件方法定义 --

        /// <summary>
        /// 定义一个事件，该事件表示某个上传文件开始上传
        /// </summary>
        public event UploadItemStart OnUploadItemStart
        {
            add
            {
                this.Events.AddHandler("OnUploadItemStart", value);
            }
            remove
            {
                this.Events.RemoveHandler("OnUploadItemStart", value);
            }
        }

        /// <summary>
        /// 定义一个事件，该事件表示某个上传文件上传完成
        /// </summary>
        public event UploadItemCompleted OnUploadItemCompleted
        {
            add
            {
                this.Events.AddHandler("OnUploadItemCompleted", value);
            }
            remove
            {
                this.Events.RemoveHandler("OnUploadItemCompleted", value);
            }
        }

        /// <summary>
        /// 定义一个事件，该事件表示某个上传文件上传发生了异常
        /// </summary>
        public event UploadItemFailedHandler OnUploadItemFailedHandler
        {
            add
            {
                this.Events.AddHandler("OnUploadItemFailedHandler", value);
            }
            remove
            {
                this.Events.RemoveHandler("OnUploadItemFailedHandler", value);
            }
        }

        /// <summary>
        /// 定义一个事件，该事件表示某个上传文件上传文件类型校验失败
        /// </summary>
        public event UploadItemValidFailed OnUploadItemValidFailed
        {
            add
            {
                this.Events.AddHandler("OnUploadItemValidFailed", value);
            }
            remove
            {
                this.Events.RemoveHandler("OnUploadItemValidFailed", value);
            }
        }


        /// <summary>
        /// 定义一个事件，该事件表示某个上传文件的上传进度发生了变化
        /// </summary>
        public event UploadItemProgressChange OnUploadItemProgressChange
        {
            add
            {
                this.Events.AddHandler("OnUploadItemProgressChange", value);
            }
            remove
            {
                this.Events.RemoveHandler("OnUploadItemProgressChange", value);
            }
        }
        #endregion

        #region -- 委托/事件辅助方法 --

        /// <summary>
        /// 解除 OnUploadItemStart,OnUploadItemCompleted, OnUploadItemFailedHandler
        /// OnUploadItemValidFailed, OnUploadItemProgressChange 五个事件，如果需要对上传文件的进度，完成，失败事件进行控制。建议先调用此方法解除默认事件
        /// </summary>
        public void UnBindEvent()
        {
            this.UnBindEvent("OnUploadItemStart", "OnUploadItemCompleted", "OnUploadItemFailedHandler", "OnUploadItemValidFailed", "OnUploadItemProgressChange");
        }

        /// <summary>
        /// 根据名称解除 OnUploadItemStart,OnUploadItemCompleted
        /// OnUploadItemFailedHandler,OnUploadItemValidFailed, OnUploadItemProgressChange 的某一个事件
        /// </summary>
        /// <param name="eventNames"></param>
        public void UnBindEvent(params string[] eventNames)
        {
            Type thisType = this.GetType();
            Delegate[] invokeList = null;
            System.Reflection.EventInfo eventInfo = null;
            foreach (string eventName in eventNames)
            {
                invokeList = DelegateEventUtility.GetObjectEventList(this, eventName);
                if (invokeList == null) { continue; }

                foreach (Delegate method in invokeList)
                {
                    eventInfo = thisType.GetEvent(eventName);
                    if (eventInfo != null) { eventInfo.RemoveEventHandler(this, method); }
                }
            }
        }

        /// <summary>
        /// 执行 OnUploadItemStart 事件绑定的委托方法
        /// </summary>
        /// <param name="uploadItem">当前开始上传对象UploadItem</param>
        public void ExcuteOnUploadItemStart(UploadItem uploadItem)
        {
            if (this.Events["OnUploadItemStart"] != null)
            {
                foreach (Delegate delgate in this.Events["OnUploadItemStart"].GetInvocationList())
                {
                    delgate.Method.Invoke(delgate.Target, new object[] { uploadItem });
                }
            }
        }

        /// <summary>
        /// 执行 OnUploadItemCompleted 事件绑定的委托方法
        /// </summary>
        /// <param name="uploadItem">当前上传完成对象UploadItem</param>
        public void ExcuteOnUploadItemCompleted(UploadItem uploadItem)
        {
            if (this.Events["OnUploadItemCompleted"] != null)
            {
                foreach (Delegate delgate in this.Events["OnUploadItemCompleted"].GetInvocationList())
                {
                    delgate.Method.Invoke(delgate.Target, new object[] { uploadItem });
                }
            }
        }

        /// <summary>
        /// 执行 OnUploadItemFailedHandler 事件绑定的委托方法
        /// </summary>
        /// <param name="uploadItem">当前上传对象UploadItem</param>
        /// <param name="exception">引发的异常信息Exception对象</param>
        /// <returns></returns>
        public bool ExcuteOnUploadItemFailedHandler(UploadItem uploadItem, Exception exception)
        {
            if (this.Events["OnUploadItemFailedHandler"] != null)
            {
                foreach (Delegate delgate in this.Events["OnUploadItemFailedHandler"].GetInvocationList())
                {
                    delgate.Method.Invoke(delgate.Target, new object[] { uploadItem, exception });
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 执行 OnUploadItemValidFailed 事件绑定的委托方法（返回是否继续上传文件标识）
        /// </summary>
        /// <param name="uploadItem">当前开始上传对象UploadItem</param>
        /// <param name="validOption">FileUploadTypeValidOption选项</param>
        public bool ExcuteOnUploadItemValidFailed(UploadItem uploadItem, FileUploadTypeValidOption validOption)
        {
            bool isContinue = false;
            if (this.Events["OnUploadItemValidFailed"] != null)
            {
                foreach (Delegate delgate in this.Events["OnUploadItemValidFailed"].GetInvocationList())
                {
                    isContinue = (bool)delgate.Method.Invoke(delgate.Target, new object[] { uploadItem, validOption });
                }
            }
            return isContinue;
        }


        /// <summary>
        /// 执行 OnUploadItemProgressChange 事件绑定的委托方法
        /// </summary>
        /// <param name="uploadItem">当前上传对象UploadItem</param>
        /// <param name="completed">已经写入磁盘的数据大小（单位：字节）</param>
        public void ExcuteOnUploadItemProgressChange(UploadItem uploadItem, long completed)
        {
            if (this.Events["OnUploadItemProgressChange"] != null)
            {
                foreach (Delegate delgate in this.Events["OnUploadItemProgressChange"].GetInvocationList())
                {
                    delgate.Method.Invoke(delgate.Target, new object[] { uploadItem, completed });
                }
            }
        }
        #endregion
    }

    /// <summary>
    /// 定义一个单个上传文件信息描述类
    /// </summary>
    public class UploadItem
    {
        /// <summary>
        /// 单个上传文件信息描述类
        /// </summary>
        /// <param name="clientKey">文件上传控件Key</param>
        /// <param name="clientName">客户端文件名</param>
        /// <param name="contentType">上传文件的ContentType</param>
        /// <param name="contentLength">文件大小</param>        
        /// <param name="inputStream">上传文件文件流</param>
        public UploadItem(string clientKey, string clientName, string contentType, long contentLength, Stream inputStream)
        {
            this.IsValid = false;
            this.FileName = clientName;
            this.ClientKey = clientKey;
            this.ClientName = clientName;

            this.ContentType = contentType;
            this.InputStream = inputStream;
            this.ContentLength = contentLength;
            this.InValidMessages = new List<string>();

            this.FileSuffix = this.FileName.Substring(this.FileName.LastIndexOf('.') + 1);
        }

        /// <summary>
        /// 获取或者设置服务器保存的文件名称（默认与属性 ClientName 一样）
        /// </summary>
        public string FileName { get; set; }

        /// <summary>
        /// 获取上传文件的大小
        /// </summary>
        public long ContentLength { get; private set; }

        /// <summary>
        /// 获取客户端上传文件Input控件名称
        /// </summary>
        public string ClientKey { get; private set; }

        /// <summary>
        /// 获取客户端上传文件的名称
        /// </summary>
        public string ClientName { get; private set; }

        /// <summary>
        /// 获取或者设置客户端上传文件的类型
        /// </summary>
        public string ContentType { get; private set; }

        /// <summary>
        /// 获取或者设置客户端上传文件的后缀名称（如文本文件,此属性为：txt）
        /// </summary>
        public string FileSuffix { get; private set; }

        /// <summary>
        /// 获取或者设置一个值，该值表示此UploadItem的文件类型是否合法（FileSuffix是否满足UploadParameter配置信息）
        /// </summary>
        public bool IsValid { get; internal set; }

        /// <summary>
        /// 获取或者设置一个值，该值表示此UploadItem验证失败的错误信息
        /// </summary>
        public List<string> InValidMessages { get; internal set; }

        /// <summary>
        /// 获取客户端上传的文件流对象
        /// </summary>
        public Stream InputStream { get; private set; }
    }
}
